﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ObjectTransposeApi.Structure;
using ObjectTransposeApi.Helper;
using System.Reflection;

namespace ObjectTransposeApi.Handler.Property
{
    /// <summary/>
    internal class EnumerationPropertyHandler : AbstractPropertyHandler
    {
        /// <summary/>
        internal override IEnumerable<QueueStructure> Process(PropertyStructure propertyStructure)
        {
            // is the destination property an enum
            if (!propertyStructure.DestinationPropertyInfo.PropertyType.IsEnum)
            {
                return base._successor.Process(propertyStructure);
            }

            // if the source property is a string then match
            if (propertyStructure.SourcePropertyInfo.PropertyType == typeof(string))
            {
                return this.ProcessString(propertyStructure);
            }

            Type underlyingType = Enum.GetUnderlyingType(propertyStructure.DestinationPropertyInfo.PropertyType);

            // does the underlying type match
            if (underlyingType == propertyStructure.SourcePropertyInfo.PropertyType)
            {
                return this.ProcessNumber(propertyStructure);
            }

            // if its a reference type then try to match by boolean
            if (!propertyStructure.SourcePropertyInfo.PropertyType.IsValueType)
            {
                return this.ProcessReferenceType(propertyStructure);
            }

            return null;
        }

        /// <summary/>
        private IEnumerable<QueueStructure> ProcessString(PropertyStructure propertyStructure)
        {
            // fetch the value
            string name = (string)ReflectionHelper.FetchPropertyValue(
                propertyStructure.SourcePropertyInfo,
                propertyStructure.Source);

            // if null then move on
            if (string.IsNullOrEmpty(name))
            {
                return base._successor.Process(propertyStructure);
            }

            // fetch the names
            string[] names =Enum.GetNames(propertyStructure.DestinationPropertyInfo.PropertyType);

            // is there a match
            if (!Array.Exists(names, value => value == name))
            {
                return base._successor.Process(propertyStructure);
            }

            // parse the 
            object destinationValue = Enum.Parse(propertyStructure.DestinationPropertyInfo.PropertyType, name, false);

            // set the destiantion property value 
            propertyStructure.DestinationPropertyInfo.SetValue(propertyStructure.Destination, destinationValue, null);

            // nothing to return
            return null;
        }

        /// <summary/>
        private IEnumerable<QueueStructure> ProcessNumber(PropertyStructure propertyStructure)
        {
            // fetch the value
            object number = ReflectionHelper.FetchPropertyValue(
                propertyStructure.SourcePropertyInfo,
                propertyStructure.Source);

            // if null then move on
            if (number == null)
            {
                return base._successor.Process(propertyStructure);
            }

            // is the value defined
            if (!Enum.IsDefined(propertyStructure.DestinationPropertyInfo.PropertyType, number))
            {
                return base._successor.Process(propertyStructure);
            }

            // set the destiantion property value 
            propertyStructure.DestinationPropertyInfo.SetValue(propertyStructure.Destination, number, null);

            // nothing to return
            return null;
        }

        /// <summary/>
        private IEnumerable<QueueStructure> ProcessReferenceType(PropertyStructure propertyStructure)
        {
            // fetch the value
            object sourcePropertyValue = ReflectionHelper.FetchPropertyValue(
                propertyStructure.SourcePropertyInfo,
                propertyStructure.Source);

            // fetch the properties on the soruce value
            PropertyInfo[] propertyInfos = ReflectionHelper.FetchGetProperties(sourcePropertyValue.GetType());

            // fetch the enum names
            string[] names = Enum.GetNames(propertyStructure.DestinationPropertyInfo.PropertyType);

            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                // is it a boolean ... if not move on
                if (propertyInfo.PropertyType != typeof(bool))
                {
                    continue;
                }

                // is it true
                if (!(bool)ReflectionHelper.FetchPropertyValue(propertyInfo, sourcePropertyValue))
                {
                    continue;
                }

                string name = Array.Find(names, value => value == propertyInfo.Name);

                // does the name match an enum value
                if (name == null)
                {
                    continue;
                }

                // parse the 
                object destinationValue = Enum.Parse(propertyStructure.DestinationPropertyInfo.PropertyType, name, false);

                // set the destiantion property value 
                propertyStructure.DestinationPropertyInfo.SetValue(propertyStructure.Destination, destinationValue, null);
            }

            return null;
        }
    }
}
